mod binary;

use binary::chunk::Prototype;

fn main() {
    if std::env::args().count() > 1 {
        let file_name = std::env::args().nth(1).unwrap();
        let file = std::fs::File::open(file_name).unwrap();

        let proto = binary::undump(std::io::BufReader::new(file));
        list(&proto)
    }
}

fn list(p: &Prototype) {
    print_header(p);
    print_code(p);
    print_detail(p);

    for p in &p.protos {
        list(p)
    }
}

fn print_header(p: &Prototype) {
    let func_type = if p.line_defined > 0 {
        "function"
    } else {
        "main"
    };

    let vararg_flag = if p.is_vararg {
        "+"
    } else {
        ""
    };

    println!("\n{} <{}:{},{}> ({} instructions)", func_type, p.source, p.line_defined, p.last_line_defined, p.code.len());
    print!("{}{} params, {} slots, {} upvalues, ", p.num_params, vararg_flag, p.max_stack_size, p.upvalues.len());
    println!("{} locals, {} constants, {} functions", p.loc_vars.len(), p.constants.len(), p.protos.len());
}

fn print_code(p: &Prototype) {
    for (pc, c) in p.code.iter().enumerate() {
        let line = if p.line_info.len() > 0 {
            p.line_info[pc].to_string()
        } else {
            String::from("-")
        };

        println!("\t{}\t[{}]\t{} ", pc+1, line, c);
    }
}

fn print_detail(p: &Prototype) {
    use binary::chunk::Constant;

    println!("constants ({}):", p.constants.len());
    for (i, k) in p.constants.iter().enumerate() {
        println!("\t{}\t{}", i + 1, 
        match k {
            Constant::Nil => String::from("nil"),
            Constant::Boolean(b) => b.to_string(),
            Constant::Integer(i) => i.to_string(),
            Constant::Number(f) => f.to_string(),
            Constant::Str(s) => format!("{:?}", s),
        })
    }
    
    println!("locals: ({}):", p.loc_vars.len());
    for (i, loc_var) in p.loc_vars.iter().enumerate() {
        println!("\t{}\t{}\t{}\t{}", i, loc_var.var_name, loc_var.start_pc + 1, loc_var.end_pc + 1);
    }

    println!("upvalues: ({}):", p.upvalues.len());
    for (i, upval) in p.upvalues.iter().enumerate() {
        println!("\t{}\t{}\t{}\t{}", i, 
        if p.upvalue_names.is_empty() {
            "-"
        } else {
            &p.upvalue_names[i]
        }, 
        upval.in_stack, upval.idx)
    }
}